"""

.. module:: spaces_test
.. moduleauthor:: @author: 

*Description*:

This module is the unit test for the Spaces module.

*Constants*:

.. seealso:: class :class:`datastore.spaces.Spaces`
"""

# Imports
#
import unittest
import logging

from datastore.resource import *
from datastore.spaces import *
from datastore.uri import *

# Constants
#
logger = logging.getLogger('yarra')
hdlr = logging.StreamHandler()
formatter = logging.Formatter('%(levelname)s %(message)s')
hdlr.setFormatter(formatter)
logger.addHandler(hdlr)
logger.setLevel(logging.INFO)

# Classes
#
class SpacesTest(unittest.TestCase):

    def setUp(self):
        self.spaces = Spaces()
        # standard definitions to pass validation in spaces.put()
        res1 = Resource(type='term:base/runtime/definition')
        self.spaces.put(Uri('term:base/runtime/definition'), res1, 
                        warning=False)
        res1 = Resource(type='term:base/runtime/definition')
        self.spaces.put(Uri('term:base/datastore/resource/type'), res1,
                        warning=False)
        res1 = Resource(type='term:base/runtime/definition')
        self.spaces.put(Uri('dc:title'), res1)
        res1 = Resource(type='term:base/runtime/definition')
        self.spaces.put(Uri('dc:description'), res1)
        res1 = Resource(type='term:base/runtime/definition')
        self.spaces.put(Uri('term:base/runtime/test'), res1)
        
    def tearDown(self):
        self.spaces = None
        
    def test_exists(self):
        """
        [Spaces] Check if a uri exists in spaces.
        """
        
        uri_list = [Uri('internal:a'),           # 0
                    Uri('internal:b'),           # 1
                    Uri('internal:c'),           # 2
                    Uri('internal:a/a'),         # 3
                    Uri('internal:a/b'),         # 4
                    Uri('internal:a/x/y/z'),     # 5
                    Uri('internal:a/x/y/z/'),    # 6
                    ]
        for u in uri_list:
            res1 = Resource(type='term:base/runtime/test')
            res1.set_var('uuu', u)
            self.spaces.put(u, res1)
        self.assertTrue(self.spaces.exists(Uri('internal:a')))
        self.assertFalse(self.spaces.exists(Uri('internal:z')))
        self.assertTrue(self.spaces.exists(Uri('internal:a/a')))
        self.assertFalse(self.spaces.exists(Uri('internal:a/x/y/z/')))

    def test_get_put_delete(self):
        """
        [Spaces] Simple get, put and delete.
        """
        
        uri_list = [Uri('internal:a'),           # 0
                    Uri('internal:b'),           # 1
                    Uri('internal:c'),           # 2
                    Uri('internal:a/a'),         # 3
                    Uri('internal:a/b'),         # 4
                    Uri('internal:a/x/y/z'),     # 5
                    Uri('internal:a/x/y/z/'),    # 6
                    ]
        for u in uri_list:
            res1 = Resource(type='term:base/runtime/test')
            res1.set_var('uuu', u)
            self.spaces.put(u, res1)
        res2 = Resource(type='term:base/runtime/test')
        res2.set_var('abc', 'this is abc')
        data = self.spaces.get(Uri('internal:a')).uuu
        self.assertEqual(data, Uri('internal:a'))
        data = self.spaces.delete(Uri('internal:a'))
        data = self.spaces.get(Uri('internal:a'))
        self.assertEqual(data, None)
        self.spaces.put(Uri('internal:a'), res2)
        data = self.spaces.get(Uri('internal:a')).abc
        self.assertEqual(data, 'this is abc')

    def test_get_collection(self):
        """
        [Spaces] Get a collection.
        """
        
        uri_list = [Uri('internal:a'),           # 0
                    Uri('internal:b'),           # 1
                    Uri('internal:c'),           # 2
                    Uri('internal:a/a'),         # 3
                    Uri('internal:a/b'),         # 4
                    Uri('internal:a/x/y/z'),     # 5
                    Uri('internal:a/x/y/z/'),    # 6
                    ]
        for u in uri_list:
            res1 = Resource(type='term:base/runtime/test')
            res1.set_var('uuu', u)
            self.spaces.put(u, res1)
        res_list = self.spaces.get_collection(Uri('internal:a/'))
        self.assertEqual(len(res_list), 2)

    def test_put_collection(self):
        """
        [Spaces] Put to a collection.
        """
        
        coll = Uri("internal:coll/")
        res1 = Resource(type='term:base/runtime/test')
        res1.set_var('uuu', 123)
        self.spaces.put(coll, res1)
        res_uri_list = self.spaces.get_collection(coll)
        self.assertEqual(self.spaces.get(res_uri_list[0]).uuu, 123)
        
    def test_remove(self):
        """
        [Spaces] Remove.
        """
        
        res1 = Resource(type='term:base/runtime/test')
        res1.set_var('abc', 'this is abc')
        self.spaces.put(Uri('internal:abc'), res1)
        res2 = Resource(type='term:base/runtime/test')
        res2.set_var('def', 'this is def')
        self.spaces.put(Uri('internal:def'), res2)
        res3 = self.spaces.remove(Uri('internal:abc'))
        self.assertEqual(res1, res3)
        data = self.spaces.delete(Uri('internal:abc'))
        data = self.spaces.get(Uri('internal:abc'))
        self.assertEqual(data, None)

    def test_search(self):
        """
        [Spaces] Test the internal _search() function.
        """
        
        dd = {'a':'aa', 'b':'bb', 'c':'cc', 'd':'dd', 'e':'ee', 'f':'ff'}
        self.assertEqual(self.spaces._search(dd, 'a'), [Uri('a')])

    def test_find_next_level(self):
        """
        [Spaces] Test the internal _find_next_level() function.
        """
        
        dd = {Uri('x/a'):'xa', 
              Uri('x/b'):'xb', 
              Uri('x/b/a'):'xba', 
              Uri('x/b/b'):'xbb', 
              Uri('x/c'):'xc',
              Uri('x/d'):'xd', 
              Uri('x/e'):'xe', 
              Uri('x/f'):'xf'}
        self.assertEqual(sorted(self.spaces._find_next_level(dd, Uri('x/b/'))), 
                [Uri('x/b/a'), Uri('x/b/b')])
        
    def test_process_properties(self):
        """
        [Spaces] Process properties get, set and delete.
        """
        
        def cb1(): return 1
        def cb2(): return 2
        def cb3(): return 3
        
        # add the same call back twice with same uri to make sure
        # that its only added once
        self.spaces.set_process(Uri('internal:a'), cb1)
        self.spaces.set_process(Uri('internal:a'), cb1)
        self.spaces.set_process(Uri('internal:a'), cb2)
        self.spaces.set_process(Uri('internal:b'), cb3)
        self.assertEqual(self.spaces.process[Uri('internal:a')],[cb1, cb2])
        self.assertEqual(self.spaces.process[Uri('internal:b')],[cb3])
        self.spaces.del_process(Uri('internal:a'), cb1)
        self.assertEqual(self.spaces.process[Uri('internal:a')],[cb2])

    def cb1(self, uri, action, res): self.test1 += 1
    def cb2(self, uri, action, res): self.test2 += 1
    def cb3(self, uri, action, res): self.test3 += 1
    def test_watch_properties(self):
        """
        [Spaces] Watch properties get, set and delete.
        """
        
        res1 = Resource(type='term:base/runtime/test')
        self.spaces.put(Uri('internal:fff1'), res1)
        self.test1 = 0
        self.test2 = 0
        self.test3 = 0
        self.spaces.set_watch(Uri('internal:fff1'), self.cb1, ['get'])
        #self.spaces.set_watch(Uri('internal:a'), self.cb2, ['get'])
        #self.spaces.set_watch(Uri('internal:b'), self.cb3, ['get'])
        result1 = self.spaces.get(Uri('internal:fff1'))
        self.assertEqual(self.test1, 1)
        self.spaces.del_watch(Uri('internal:fff1'), self.cb1)
        result1 = self.spaces.get(Uri('internal:fff1'))
        self.assertEqual(self.test1, 1)

    def test_find(self):
        """
        [Spaces] Find one or more uri's in spaces.
        """
        
        uri_list = [Uri('internal:a'),           # 0
                    Uri('internal:b'),           # 1
                    Uri('internal:c'),           # 2
                    Uri('internal:a/a'),         # 3
                    Uri('internal:a/b'),         # 4
                    Uri('internal:a/x/y/z'),     # 5
                    Uri('internal:a/x/y/z/'),    # 6
                    ]
        for u in uri_list:
            res1 = Resource(type='term:base/runtime/test')
            res1.set_var('uuu', u)
            self.spaces.put(u, res1)
        # ignore the collection resource
        self.assertEqual(self.spaces.find(Uri('internal:a'))[0:3],uri_list[3:6])
        self.assertEqual(self.spaces.find(Uri('internal:b')), [])
        find_uri_list = self.spaces.find(Uri('internal:a/x/y/z'))
        self.assertTrue(
            str(find_uri_list[0].full()).startswith(str(uri_list[6].full())))
        self.assertEqual(self.spaces.find(Uri('internal:zzz')), [])
        # the seven above plys the 5 created in setUp()
        self.assertEqual(len(self.spaces.find(Uri())), 12)

    def test_watch(self):
        """
        [Spaces] Watch with callback function
        """
        
        print()
        def cb1(uri, action, res): 
            print("user watch callback %s %s" % (uri, action))
        def cb2(uri, action, res): 
            print("user watch callback %s %s" % (uri, action))
        def cb3(uri, action, res): 
            print("user watch callback %s %s" % (uri, action))
        self.spaces.set_watch(Uri('internal:'), cb1, filter=['get'])
        self.spaces.set_watch(Uri('internal:d'), cb2, filter=['delete'])
        self.spaces.set_watch(Uri('internal:a/x'), cb3)

        uri_list = [Uri('internal:a'),           # 0
                    Uri('internal:b'),           # 1
                    Uri('internal:c'),           # 2
                    Uri('internal:a/a'),         # 3
                    Uri('internal:a/b'),         # 4
                    Uri('internal:a/x/y/z'),     # 5
                    Uri('internal:a/x/y/z/'),    # 6
                    ]
        for u in uri_list:
            res1 = Resource(type='term:base/runtime/test')
            res1.set_var('uuu', u)
            self.spaces.put(u, res1)
        res2 = Resource(type='term:base/runtime/test')
        res2.set_var('abc', 'this is abc')
        data = self.spaces.get(Uri('internal:a')).uuu
        self.assertEqual(data, Uri('internal:a'))
        data = self.spaces.delete(Uri('internal:a'))
        data = self.spaces.get(Uri('internal:a'))
        self.assertEqual(data, None)
        self.spaces.put(Uri('internal:a'), res2)
        data = self.spaces.get(Uri('internal:a')).abc
        self.assertEqual(data, 'this is abc')
        res = self.spaces.get(Uri('internal:a/x/y/z'))
        res = self.spaces.get(Uri('internal:a/x/y'))
        res3 = Resource(type='term:base/runtime/test')
        self.spaces.put(Uri('internal:d'), res3)
        res = self.spaces.delete(Uri('internal:d'))

    def TODO_test_process(self):
        """
        [Spaces] Process with get, put and delete.
        """
        
        def w(cb, uri, action, resource):
            print("user watch callback %s %s" % (uri, action))
        def cb1(): return 1
        def cb2(): return 2
        def cb3(): return 3
        self.spaces.set_watch(Uri('internal:'), cb1)
        self.spaces.set_watch(Uri('internal:b'), cb2)
        self.spaces.set_watch(Uri('internal:a/x'), cb3)

        uri_list = [Uri('internal:a'),           # 0
                    Uri('internal:b'),           # 1
                    Uri('internal:c'),           # 2
                    Uri('internal:a/a'),         # 3
                    Uri('internal:a/b'),         # 4
                    Uri('internal:a/x/y/z'),     # 5
                    Uri('internal:a/x/y/z/'),    # 6
                    ]
        for u in uri_list:
            res1 = Resource(type='term:base/runtime/test')
            res1.set_var('uuu', u)
            self.spaces.put(u, res1)
        res2 = Resource(type='term:base/runtime/test')
        res2.set_var('abc', 'this is abc')
        data = self.spaces.get(Uri('internal:a')).uuu
        self.assertEqual(data, Uri('internal:a'))
        data = self.spaces.delete(Uri('internal:a'))
        data = self.spaces.get(Uri('internal:a'))
        self.assertEqual(data, None)
        self.spaces.put(Uri('internal:a'), res2)
        data = self.spaces.get(Uri('internal:a')).abc
        self.assertEqual(data, 'this is abc')
        res = self.spaces.get(Uri('internal:a/x/y/z'))

    def test_copy_01(self):
        """
        [spaces] test copying a resource
        """
        
        res1 = Resource(type='term:base/runtime/test')
        res1.aaa = 10
        self.spaces.put(Uri('internal:test'), res1)
        res2 = self.spaces.get(Uri('internal:test'))
        res1.aaa = 20
        self.assertNotEqual(res1.aaa, res2.aaa)

    def test_counter_01(self):
        """
        [spaces] test copying a resource
        """
        
        collection = Uri('internal:test-counter/')
        res1 = Resource(type='term:base/runtime/test')
        res1.aaa = 30
        self.spaces.put(collection, res1)
        res2 = Resource(type='term:base/runtime/test')
        res2.aaa = 10
        self.spaces.put(collection, res2)
        res3 = Resource(type='term:base/runtime/test')
        res3.aaa = 20
        self.spaces.put(collection, res3)
        # This should be sorted as res1, res2, res3
        res_list = self.spaces.get_collection(collection)
        for u in res_list:
            print(str(u), u.aaa)
        self.assertTrue(res_list[0] < res_list[1])
        self.assertTrue(res_list[1] < res_list[2])
        self.assertTrue(self.spaces.get(res_list[0]).aaa > 
                        self.spaces.get(res_list[1]).aaa)
        self.assertTrue(self.spaces.get(res_list[2]).aaa > 
                        self.spaces.get(res_list[1]).aaa)

if __name__ == "__main__":
    #import sys;sys.argv = ['', 'Test.testName']
    unittest.main()